/*
 * Matthew Cramer, Jesse Martinez, Marc Mendoza 
 * CSE 325 Lab 3
 * File: Scheduler.c
 * Description: Modules that handles the Scheduling for the OS.
 *              Implements Round Robin or Priority Scheduling when called.
 *
 */

#include "Scheduler.h"
#include "../QueueManager/QueueManager.h"
#include "../ProcessManager/ProcessManager.h"
#define MAX_PROCESSES 20
#define MAX_QUEUE_SIZE 20
#define MAXTQ 3

int createProcess(int, int, int, int, int, int);
int Scheduler(int, int, int);
int highestPriority();
void initSchedule();
int findHighPriority();
void updateGroupTQ();
int lcm(int, int);
process_control_block* tempQueue;
int groupSize[] ={0,0,0,0};
int groupMaxTQ[] = {0, 0, 0, 0};
int newProc=0;

//Create a process to be placed in the system
//The process will automatically be placed in the ready queue if it is not full
int createProcess(int psw,int pageTable, int reg0, int reg1, int reg2, int group)
{
    //Increment group counter for FSG scheduler
    if(group==1)
        groupSize[0]++;
    else if(group==2)
        groupSize[1]++;
    else if(group==3)
        groupSize[2]++;
    else if(group==4)
        groupSize[3]++;
    
	//set boolean check for new Process
	newProc=1;
    int returnValue = newProcess(psw,pageTable,reg0,reg1,reg2,group);
    return returnValue;
}

//Clear the group count when queues gets reset
void initSchedule()
{
    groupSize[0] = 0;
	groupSize[1] = 0;
	groupSize[2] = 0;
	groupSize[3] = 0;
}


/*
 * Scheduler: Receives transition commands from the testing interface
 */
int Scheduler(int algorithm, int command, int PID)
{
    int returnValue = 0;
    //If command is UNWAIT
    if(command == 1)
    {
        if(getSizeOfQueue(4) > 0 && getSizeOfQueue(2) < MAX_QUEUE_SIZE)
        {
            //Send command to Process Manager
            returnValue = processManagerFunction(command,PID);
        }
        else
        {
            returnValue = -3;
        }
    }
    //If command is EOQUANTUM
    else if(command == 2)
    {
        if(getSizeOfQueue(2) < MAX_QUEUE_SIZE && getSizeOfQueue(3) == 1)
        {
            //Increment Priority and set TQ to zero
            modifyPCB(1);
            //Send command to Process Manager
            returnValue = processManagerFunction(command,PID);
        }
        else
        {
            returnValue = -4;
        }
    }
    //If command is EOLIFE
    else if(command == 3)
    {
        if(getSizeOfQueue(3)==1)
        {
            //Send command to Process Manager
            returnValue = processManagerFunction(command,PID);
        }
        else
        {
            returnValue = -5;
        }
    }
    //If command is WAIT
    else if(command == 4)
    {
        if(getSizeOfQueue(3) == 1 && getSizeOfQueue(4) < MAX_QUEUE_SIZE)
        {
            //Increment Priority and set TQ to zero
            modifyPCB(1);
            //Send command to Process Manager
            returnValue = processManagerFunction(command,PID);
        }
        else
        {
            returnValue = -6;
        }
    }
    //If command is GO, initiate scheduler
    else
    {
        //For Fair Share Group Scheduler
        if(algorithm==1)
        {
			//if there is a process is running
            if(getSizeOfQueue(3) == 1)
		    {
				tempQueue = getQueue(3);
				int head = getHead(3);
				modifyPCB(3);
				
				//if the TQ is less than the max tq of its group, increment TQ
				if(tempQueue[head].TQ < groupMaxTQ[tempQueue[head].group-1])
				{
					returnValue = -11;
				}
				//if the process is done
				else
				{
					//Set TQ to zero
					modifyPCB(2);
					//EOQuantum
					processManagerFunction(2,0);
					
					//if a new process was created during the running processs' run time
					if(newProc == 1)
					{
						//reset boolean check
						newProc = 0;
						//TQ are updated for each group
						updateGroupTQ();
						returnValue = goFunction(1, 0);
					}
					//nothing extra has to be done before go is called
					else
						returnValue = goFunction(1, 0);
				}
		    }
			//there is no process in running
		    else
		    {
				//if a new process was created before this go statement but after the last call
				if(newProc == 1)
				{
					//reset boolean check
					newProc = 0;
					//TQ are updated for each group
					updateGroupTQ();
					returnValue = goFunction(1, 0);
				}
				//nothing extra has to be done before go is called
				else
					returnValue = goFunction(1, 0);
		    }
        }
        //For Priority Queue
        else if(algorithm==2)
        {
            //If there is a process
            if(getSizeOfQueue(3) == 1)
            {
                tempQueue = getQueue(3);
                int head = getHead(3);
                //If the TQ is less than TQMAX, keep it in running, increment TQ
                if(tempQueue[head].TQ < MAXTQ)
                {
                    //Increment TQ
                    modifyPCB(3);
                    returnValue = -11;
                }
                //If the TQ over TQMAX, move it to ready, reset TQ of process
                else
                {
                    //DeIncrement Priority and set TQ to zero
                    modifyPCB(2);
                    //EOQUANTUM
                    int temp = processManagerFunction(2,0);
                    
                    //Call function to place highest priority in Running
                    returnValue = highestPriority();
                }
            }
            //If there is not a process, find highest priority and place in running
            else
            {
                //Get highest priority if Ready not empty
                returnValue = highestPriority();
            }
            //Increment Ready time counter for all processes in Ready queue
            starvationFunction();
        }   
    }
    return returnValue;
}

//Move Highest Priority to Running from Ready
int highestPriority()
{
    int returnValue=0;
    int sizeOfReady = getSizeOfQueue(2);
    //If there are processes in the Ready queue
    if(sizeOfReady > 0)
    {        int highIndex = findHighPriority();
        //Move highest priority from running to ready
        if(highIndex != -2)
        {
            returnValue = goFunction(3,tempQueue[highIndex].pid);
        }
        else
        {
            returnValue = -12;
        }
    }
    //There are no processes in the Ready queue
    else
    {
        //There is nothing in the ready Queue to be moved to running
        returnValue = -12;
    }
    return returnValue;
}

//Find the highest priority in the ready queue
int findHighPriority()
{
    int highIndex=-2;
    int highPri=0;
    int i=getHead(2);
    int sizeOfReady=getSizeOfQueue(2);
    int j=0;
    tempQueue = getQueue(2);
    //Find the highest priority in the queue
    while(j < sizeOfReady)
    {
        if(tempQueue[i].priority > highPri)
        {
            highPri =  tempQueue[i].priority;
            highIndex = i;
        }
        i=tempQueue[i].nextIndex;
        j++;
    }
    return highIndex;
}

//updates the max TQ for each group when a new process is created and there are no
//running process
void updateGroupTQ()
{
	int tempMax = lcm(lcm(groupSize[0], groupSize[1]), lcm(groupSize[2], groupSize[3]));
	int i;
	
	for(i = 0; i < 4; i++)
	{
		//when there are no processes in a group, its Max TQ is zero
		if(groupSize[i] == 0)
			groupMaxTQ[i] = 0;
		//otherwise the groupMaxTQ is the LCM of the groups sizes divided by the size of
		// the current groups' size
		else
			groupMaxTQ[i] = tempMax / groupSize[i];
	}
}

//Finds the lowest common multiple
int lcm(int a, int b)
{
	int c;
	//a and b are switched to be sure a is greater than b
	if(a < b)
	{
		c = a;
		a = b;
		b = c;
	}
	
	if(b == 0)
		return a;
	else if(a%b == 0)
		return a;
	else
		return ((a*b) / (a%b));
}